#pragma once

#include <optional>
#include <GL\glew.h>
#include "pipeline_impl.h"
#include <memory>

namespace blu
{
	namespace impl
	{
		class BoundedIndexBuffer
		{
		private:
			const void* _offset;
			::GLenum _indexType;
		public:
			BoundedIndexBuffer(const void *offset_, ::GLenum indextype_)
				:_offset(offset_), _indexType(indextype_)
			{

			}

			const void* offset() const
			{
				return _offset;
			}

			::GLenum indexType() const
			{
				return _indexType;
			}
		};

		class RecordingContext
		{
		private:
			std::optional<BoundedIndexBuffer> _curIBuffer;
			std::optional<std::reference_wrapper<Pipeline>> _curPipeline;
		public:
			std::optional<BoundedIndexBuffer>& getCurrentIndexBuffer()
			{
				return _curIBuffer;
			}

			std::optional<std::reference_wrapper<Pipeline>>& getCurrentPipeline()
			{
				return _curPipeline;
			}

			void notifyBindingIndexBuffer(const BoundedIndexBuffer &bib)
			{
				_curIBuffer = bib;
			}
		};

		class GLContext
		{
		private:
			bool _existVAO = false;
		public:
			void makeVAOIfNo()
			{
				if (!_existVAO)
				{
					::GLuint hVAO;
					::glGenVertexArrays(1, &hVAO);
					::glBindVertexArray(hVAO);
				}
			}
			bool destroyCurrentVAO()
			{
				auto retval = _existVAO;
				::glBindVertexArray(0);
				_existVAO = false;
				return retval;
			}
		};

		class ExecuteContext
		{
		private:
			std::reference_wrapper<Pipeline> _curPipeline;
			GLContext _glCtxt;
		public:
			Pipeline& getPipeline()
			{
				return _curPipeline.get();
			}

			GLContext& getGLContext()
			{
				return _glCtxt;
			}

			void BindPipeline(Pipeline &pipline_)
			{
				_curPipeline = pipline_;
			}
		};

		class GLCommand
		{
		public:
			virtual void execute(ExecuteContext &) = 0;
			virtual ~GLCommand()
			{

			}
		};

		class CmdBuffer
		{
		private:
			RecordingContext _recCtxt;
			std::vector<std::unique_ptr<GLCommand>> _commands;
		public:
			RecordingContext& getRecordingContext()
			{
				return _recCtxt;
			}
			void push(std::unique_ptr<GLCommand> &&pcmd)
			{
				_commands.push_back(std::move(pcmd));
			}
		};
	}
}